package com.easybuilder.core.table.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.easybuilder.common.annotation.Transactional;
import com.easybuilder.common.base.entity.BaseEntity;
import com.easybuilder.common.base.entity.DynamicEntity;
import com.easybuilder.common.utils.DBUtil;
import com.easybuilder.common.utils.StringUtil;
import com.easybuilder.core.function.entity.SysForm;
import com.easybuilder.core.function.entity.SysFormItem;
import com.easybuilder.core.function.entity.SysGrid;
import com.easybuilder.core.function.entity.SysGridItem;
import com.easybuilder.core.function.service.SysFormItemService;
import com.easybuilder.core.function.service.SysFormService;
import com.easybuilder.core.function.service.SysGridItemService;
import com.easybuilder.core.function.service.SysGridService;
import com.easybuilder.core.table.dao.TableDao;
import com.easybuilder.core.table.dto.TableTree;
import com.easybuilder.core.table.entity.SysTable;
import com.easybuilder.core.table.entity.SysTableInfo;
import com.easybuilder.core.table.service.TableInfoService;
import com.easybuilder.core.table.service.TableService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import static com.alibaba.druid.util.FnvHash.Constants.AUTO_INCREMENT;

/**
 * @author ZXS
 * @version 1.0
 * @description 类
 * @date 2021-5-25 23:29
 */
@Service
public class TableServiceImpl implements TableService {
    @Resource
    private TableDao tableDao;
    @Autowired
    private TableInfoService tableInfoService;
    @Autowired
    private SysGridService gridService;
    @Autowired
    private SysGridItemService gridItemService;
    @Autowired
    private SysFormService formService;
    @Autowired
    private SysFormItemService formItemService;

    // 修改表结构锁
    private static final Object alter_lock = new Object();

    /**
     * @description: 创建模块或表
     * @param:
     * @param sysTable
     * @return:
     * @author ZXS
     * @date: 2021-5-31 21:55
     */
    @Override
    @Transactional
    public SysTable save(SysTable sysTable) {
        // 如果id为空则创建
        if (sysTable.getId() == null) {
            int insert = tableDao.insertSelective(sysTable);
            // 如果是创建表成功，同时给tableInfo中插入默认系统字段
            if (sysTable.getType() == 1 && insert > 0) {
                // 初始化默认系统字段
                tableInfoService.createSystemColumn(sysTable.getId());
                // 生成物理表结构
                create(sysTable);
                List<SysTableInfo> sysTableInfos = tableInfoService.selectByTableId(sysTable.getId());
                // 创建默认系统字段列表
                createGridAndGridItem(sysTable, sysTableInfos);
                // 创建默认系统字段表单
                createFormAndFormItem(sysTable, sysTableInfos);
            }
        } else {
            // 先查询原来表的名称
            SysTable oldSysTable = tableDao.selectByPrimaryKey(sysTable.getId());
            // 如果表名发生了变化，更新物理表名
            if (!oldSysTable.getTableCode().equals(sysTable.getTableCode())) {
                boolean update = DBUtil.executeUpdate("RENAME TABLE " + oldSysTable.getTableCode() + " TO " + sysTable.getTableCode());
                // 如果物理表名更新成功，则更新table中的表名
                if (update) {
                    tableDao.updateByPrimaryKeySelective(sysTable);
                }
            }
        }
        return sysTable;
    }

    /** 创建系统默认表单和表单项 */
    private void createFormAndFormItem(SysTable sysTable,List<SysTableInfo> sysTableInfos) {
        SysForm form = new SysForm();
        form.setTableId(sysTable.getId());
        form.setFormName(sysTable.getTableName());
        form.setFormCode(sysTable.getTableCode());
        int insert = formService.insertSelective(form);
        // 如果form主表插入成功，插入formItem
        if (insert > 0) {
            sysTableInfos.forEach(tableInfo -> {
                SysFormItem formItem = new SysFormItem();
                formItem.setFormId(form.getId());
                formItem.setLabel(tableInfo.getComment());
                formItem.setName(StringUtil.column2Field(tableInfo.getColumnName()));
                formItem.setSort(tableInfo.getSort());
                formItem.setWidth(100);
                formItemService.insertSelective(formItem);
            });
        }
    }

    /** 创建默认系统列表和列表项 */
    private void createGridAndGridItem(SysTable sysTable, List<SysTableInfo> sysTableInfos) {
        SysGrid grid = new SysGrid();
        grid.setTableId(sysTable.getId());
        grid.setGridName(sysTable.getTableName());
        grid.setGridCode(sysTable.getTableCode());
        int insertGrid = gridService.insertSelective(grid);
        // 如果grid主表插入成功，插入gridItem
        if (insertGrid > 0) {
            // 查询tableInfo
            sysTableInfos.forEach(tableInfo -> {
                SysGridItem gridItem = new SysGridItem();
                gridItem.setGridId(grid.getId());
                gridItem.setLabel(tableInfo.getComment());
                gridItem.setProp(StringUtil.column2Field(tableInfo.getColumnName()));
                gridItem.setSort(tableInfo.getSort());
                gridItem.setWidth(100);
                gridItem.setAlign("center");
                gridItemService.insertSelective(gridItem);
            });
        }
    }


    /**
     * @description: 检查模块是否已存在
     * @param:
     * @param sysTable
     * @return:
     * @author ZXS
     * @date: 2021-5-31 21:56
     */
    @Override
    public boolean checkModelCodeExist(SysTable sysTable) {
        SysTable table = new SysTable();
        table.setTableCode(sysTable.getTableCode());
        table.setType(sysTable.getType());
        int count = tableDao.selectCount(table);
        // 如果表还未保存，并且已经存在相同编码，则认为编码重复
        if (sysTable.getId() == null && count > 0) {
            return true;
        }
        return false;
    }

    /**
     * @Author zhangxingshuai
     * @Description //TODO 获取模块或表的树形结构
     * @Date 10:57 2021/8/23
     * @Param [id]
     * @return java.util.List<com.alibaba.fastjson.JSONObject>
     **/
    @Override
    public List<TableTree> getTree() {
        // 1.先获取当前节点
        SysTable sysTable = new SysTable();
        sysTable.setParentId(0L);
        List<SysTable> parents = tableDao.select(sysTable);
        List<TableTree> trees = ConvertTable2Tree(parents);
        // 2.遍历所有一级父节点，查询下面的子节点并赋值
        if (trees != null && trees.size() > 0) {
            trees.forEach(parent -> {
                setChildList(parent);
            });
        }
        return trees;
    }

    /** 删除 */
    @Override
    public int delete(Long id) {
        // 删除模块或表
        int delete = tableDao.deleteByPrimaryKey(id);
        // 删除表对应的tableInfo数据
        tableInfoService.deleteByTableId(id);
        return delete;
    }

    /** 创建表结构 */
    @Override
    public boolean create(SysTable sysTable) {
        // 表编码（对应数据库表名）
        String tableCode = sysTable.getTableCode();
        Long id = sysTable.getId();
        // 查询tableInfo的字段
        List<SysTableInfo> list = tableInfoService.selectByTableId(id);
        StringBuilder sb = new StringBuilder("CREATE TABLE `" + tableCode +"`  (");
        String primary_key = "";
        String auto_increment_extra = "";
        for (SysTableInfo tableInfo : list) {
            // 字段名
            String columnName = tableInfo.getColumnName();
            String columnType = tableInfo.getColumnType();
            Integer length = tableInfo.getLength();
            Integer notNull = tableInfo.getNotNull();
            String defaultValue = tableInfo.getDefaultValue();
            String comment = tableInfo.getComment();
            String columnKey = tableInfo.getColumnKey();
            String extra = tableInfo.getExtra();
            // 存在主键
            if ("PRI".equals(columnKey)) {
                primary_key = columnName;
            }
            // 存在自增
            if ("AUTO_INCREMENT".equals(extra)) {
                auto_increment_extra = extra;
            }
            sb.append("\n").append("`").append(columnName).append("` ").append(columnType).append("(").append(length).append(") ");
            // 设置字符集（varchar类型text类型等）
            if ("varchar".equals(columnType)) {
                sb.append("CHARACTER SET utf8 ").append("COLLATE utf8_general_ci ");
            }
            // 设置notnull
            if (notNull != null && notNull.equals(1)) {
                sb.append("NOT NULL ");
            } else {
                sb.append("NULL ");
            }
            // 设置扩展信息（自增）
            sb.append(extra != null ? extra : "");
            // 设置默认值
            if (!"PRI".equals(columnKey)) {
                sb.append(" DEFAULT ");
                if (StringUtil.isEmpty(defaultValue)) {
                    sb.append("NULL ");
                } else {
                    sb.append("'").append(defaultValue).append("' ");
                }
            }
            // 设置描述信息
            sb.append(" COMMENT").append("'").append(comment).append("',");
        }
        // 如果存在主键，则设置主键
        if (StringUtil.isNotEmpty(primary_key)) {
            sb.append("\n").append("PRIMARY KEY (`").append(primary_key).append("`) USING BTREE ");
        }
        sb.append("\n").append(")").append(" ENGINE = InnoDB ");
        // 如果存在自增
        if (StringUtil.isNotEmpty(auto_increment_extra)) {
            sb.append("AUTO_INCREMENT = 0 ");
        }
        sb.append("CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;");

        return DBUtil.executeUpdate(sb.toString());
    }

    /** 删除表结构 */
    @Override
    public boolean drop(SysTable sysTable) {
        boolean result = true;
        // 如果删除的是目录
        if (sysTable.getType().equals(0)) {

        } else { // 如果删除的是表
            String tableCode = sysTable.getTableCode();
            String sql = "drop table " + tableCode;
            // 删除物理表结构
            result = DBUtil.executeUpdate(sql);
            if (result) {
                tableDao.delete(sysTable);
                // 删除table/tableInfo
                tableDao.casecadeDeleteByTableId(sysTable.getId());
                // 删除grid/gridItem
                gridService.casecadeDeleteByTableId(sysTable.getId());
                // 删除form/formItem
                formService.casecadeDeleteByTableId(sysTable.getId());
            }

        }
       return true;
    }

    /** 修改表结构 */
    @Override
    public boolean alter(SysTable sysTable) {
        synchronized (alter_lock) {
            String tableCode = sysTable.getTableCode();
            // 1.查询当前表在tableinfo中的字段
            List<SysTableInfo> sysTableInfos = tableInfoService.selectByTableId(sysTable.getId());
            // 2.查询当前实际表结构的字段
            List<DynamicEntity> dynamicEntities = DBUtil.selectList("show columns from " + tableCode);
            // 转换数据格式，便于比对
            List<SysTableInfo> reallyColumns = convertToSysTableInfo(dynamicEntities);
            // 3 比对两批数据，筛选出修改字段、删除字段、新增字段（顺序必须按照修改、删除、新增执行）
            // 3.0 字段名发生改变的数据
            List<SysTableInfo> changeColumnNames = getChangeColumnNames(sysTableInfos, reallyColumns);
            // 3.1修改的字段
            List<SysTableInfo> modifyColumns = getModifyColumns(sysTableInfos, reallyColumns);
            // 3.2删除的字段
            List<SysTableInfo> dropColumns = getDropColumns(sysTableInfos, reallyColumns);
            // 3.3新增的字段
            List<SysTableInfo> addColumns = getAddColumns(sysTableInfos, reallyColumns);
            // 4.根据比对结果，生成alter table语句（执行顺序与mysql保持一致：删、改、增）
            StringBuffer alterSql = new StringBuffer("ALTER TABLE ").append(tableCode).append("\n");
            // 4.0 生成修改字段名称语句
            if (changeColumnNames.size() > 0) {
                changeColumnNames.forEach(item -> {
                    // CHANGE COLUMN `add_field` `modify_field` int(10) NULL DEFAULT NULL COMMENT '添加字段' AFTER `id`,
                    alterSql.append("\n CHANGE COLUMN `").append(item.getColumnNameOld()).append("`  `").append(item.getColumnName()).append("` ");
                    alterSql.append(item.getColumnType()).append("(").append(item.getLength()).append(") ").append(item.getNotNull().equals(0) ? "NULL " : "NOT NULL ").append("DEFAULT ");
                    if (StringUtil.isEmpty(item.getDefaultValue())) {
                        alterSql.append("NULL ");
                    } else {
                        alterSql.append("'").append(item.getDefaultValue()).append("' ");
                    }
                    alterSql.append("COMMENT '").append(item.getComment()).append("',");
                });
            }
            // 4.1 生成删除字段语句
            if (dropColumns.size() > 0) {
                dropColumns.forEach(item -> {
                    alterSql.append("\n DROP COLUMN `").append(item.getColumnName()).append("`,");
                });
            }
            // 4.2 生成修改字段语句
            if (modifyColumns.size() > 0) {
                modifyColumns.forEach(item -> {
                    alterSql.append("\n MODIFY COLUMN `").append(item.getColumnName()).append("` ").append(item.getColumnType()).append("(");
                    alterSql.append(item.getLength()).append(") ").append(item.getNotNull().equals(0) ? "NULL " : "NOT NULL ").append("DEFAULT ");
                    if (StringUtil.isEmpty(item.getDefaultValue())) {
                        alterSql.append("NULL ");
                    } else {
                        alterSql.append("'").append(item.getDefaultValue()).append("' ");
                    }
                    alterSql.append("COMMENT '").append(item.getComment()).append("',");
                });
            }
            // 4.3 生成新增字段语句
            if (addColumns.size() > 0) {
                addColumns.forEach(item -> {
                    alterSql.append("\n ADD COLUMN `").append(item.getColumnName()).append("` ");
                    alterSql.append(item.getColumnType()).append("(").append(item.getLength()).append(") ");
                    alterSql.append(item.getNotNull().equals(0) ? "NULL " : "NOT NULL ").append("DEFAULT ");
                    if (StringUtil.isEmpty(item.getDefaultValue())) {
                        alterSql.append("NULL ");
                    } else {
                        alterSql.append("'").append(item.getDefaultValue()).append("' ");
                    }
                    alterSql.append("COMMENT '").append(item.getComment()).append("',");
                });
            }
            alterSql.deleteCharAt(alterSql.lastIndexOf(",")).append(";");
            // 执行alter语句
            boolean alterResult = DBUtil.executeUpdate(alterSql.toString());
            // 如果执行alter成功，修改tableInfo中的column_name_old与column_name一致
            if (alterResult) {
                tableInfoService.synchColumnOldName(sysTable.getId());
//                modifyColumns.forEach(item -> {
//                    item.setColumnNameOld(item.getColumnName());
//                    tableInfoService.updateByPrimaryKeySelective(item);
//                });
            }
            return alterResult;
        }
    }

    /** 根据id获取table */
    @Override
    public SysTable getTableById(Long id) {
        SysTable table = new SysTable();
        table.setId(id);
        return tableDao.selectOne(table);
    }

    /** 获取名称发生改变的字段 **/
    private List<SysTableInfo> getChangeColumnNames(List<SysTableInfo> sysTableInfos, List<SysTableInfo> reallyColumns) {
        List<SysTableInfo> result = new ArrayList<>();
        Iterator<SysTableInfo> iterator = sysTableInfos.iterator();
        while (iterator.hasNext()) {
            SysTableInfo tableInfo = iterator.next();
            // 字段名
            String columnName = tableInfo.getColumnName();
            // 旧字段名，与数据库字段名保持一致
            String columnNameOld = tableInfo.getColumnNameOld();
            // 如果字段名发生改变，则为修改字段
            if (!columnName.equals(columnNameOld)) {
                // 添加至修改字段集合
                result.add(tableInfo);
                // 从tableInfo中移除当前字段
                iterator.remove();
                // 从真实表字段集合中移除修改字段
                Iterator<SysTableInfo> iterator1 = reallyColumns.iterator();
                while (iterator1.hasNext()) {
                    SysTableInfo item = iterator1.next();
                    if (item.getColumnName().equals(columnNameOld)) {
                        iterator1.remove();
                    }
                }

            }
        }
        return result;
    }

    /** 获取发生修改的字段（1.tableInfo字段新旧名称不一致；2.tableInfo字段新旧名称一致，但是字段的其他信息不一致，例如长度等） */
    private List<SysTableInfo> getModifyColumns(List<SysTableInfo> sysTableInfos, List<SysTableInfo> reallyColumns) {
        List<SysTableInfo> result = new ArrayList<>();
        Iterator<SysTableInfo> iterator = sysTableInfos.iterator();
        while (iterator.hasNext()) {
            SysTableInfo tableInfo = iterator.next();
            // 字段名
            String columnName = tableInfo.getColumnName();
            // 旧字段名，与数据库字段名保持一致
            String columnNameOld = tableInfo.getColumnNameOld();
            // 如果字段名未发生改变，则为修改字段
            if (columnName.equals(columnNameOld)) {
                Iterator<SysTableInfo> iterator1 = reallyColumns.iterator();
                while (iterator1.hasNext()) {
                    SysTableInfo item = iterator1.next();
                    if (item.getColumnName().equals(columnNameOld)) {
                        // 如果数据库的字段信息与tableInfo字段信息不一致，则认为发送改变
                        if (!item.equals(tableInfo)) {
                            // 添加到修改字段集合
                            result.add(tableInfo);
                            // 从tableInfo中移除当前字段
                            iterator.remove();
                            // 从真实表字段集合中移除修改字段
                            iterator1.remove();
                        } else {
                            // 如果比对信息一致，说明字段未发生变化，移除未发生变化的字段
                            iterator.remove();
                            iterator1.remove();
                        }
                    }
                }
            }
        }
        return result;
    }

    /** 获取发生删除的字段（排除tableInfo中的修改字段后，真实表中有该字段，tableInfo中无该字段） */
    private List<SysTableInfo> getDropColumns(List<SysTableInfo> sysTableInfos, List<SysTableInfo> reallyColumns) {
        List<SysTableInfo> result = new ArrayList<>();
        Iterator<SysTableInfo> iterator = reallyColumns.iterator();
        while (iterator.hasNext()) {
            SysTableInfo tableInfo = iterator.next();
            // 库表中真实存在的字段
            String columnName = tableInfo.getColumnName();
            // 如果当前库表中的字段，在tableInfo中不存在，则认为需要删除
            if (!isColumnExist(sysTableInfos, columnName)) {
                // 添加到删除字段集合
                result.add(tableInfo);
                // 移除真实库中需要删除的字段，减少后续比对次数
                iterator.remove();
            }
        }
        return result;
    }

    /** 获取新增字段：（排除修改字段和删除字段后，tableInfo中有，真实库表中没有的字段，为新增字段） */
    private List<SysTableInfo> getAddColumns(List<SysTableInfo> sysTableInfos, List<SysTableInfo> reallyColumns) {
        List<SysTableInfo> result = new ArrayList<>();
        // 排除掉修改字段、删除字段后，tableInfo中剩余的便是新增字段
        return sysTableInfos;
    }

    /** 判断指定字段在集合中是否存在 */
    private boolean isColumnExist(List<SysTableInfo> list, String columName) {
        boolean exist = false;
        if (list != null && list.size() > 0) {
            for (SysTableInfo sysTableInfo : list) {
                if (columName.equals(sysTableInfo.getColumnName())) {
                    return true;
                }
            }
        }
        return exist;
    }

    /** 把表的列信息数据转为SysTableInfo */
    private List<SysTableInfo> convertToSysTableInfo(List<DynamicEntity> list) {
        List<SysTableInfo> result = new ArrayList<>();
        list.forEach(item -> {
            // 字段名称
            String field = item.getString("Field");
            // 字段类型（varchar（50）/ datetime）
            String type = item.getString("Type");
            // 是否可以为空
            String isNull = item.getString("Null");
            // 是否特殊键
            String key = item.getString("Key");
            // 默认值
            String defaultValue = item.getString("Default");
            // 额外信息
            String extra = item.getString("Extra");

            SysTableInfo tableInfo = new SysTableInfo();
            tableInfo.setColumnName(field);
            tableInfo.setNotNull("NO".equals(isNull) ? 1 : 0);
            tableInfo.setColumnKey(key);
            tableInfo.setDefaultValue(defaultValue);
            tableInfo.setExtra(extra);
            // 如果携带括号，需要解析字段长度
            if (type.contains("(")) {
                String columnType = type.substring(0, type.indexOf("("));
                Integer length = Integer.valueOf(type.substring(type.indexOf("(") + 1, type.indexOf(")")));
                tableInfo.setColumnType(columnType);
                tableInfo.setLength(length);
            } else {
                tableInfo.setColumnType(type);
                tableInfo.setLength(0);
            }

            result.add(tableInfo);
        });
        return result;
    }

    /** 给节点设置子节点 */
    private void setChildList(TableTree parent) {
        SysTable sysTable = new SysTable();
        sysTable.setParentId(parent.getId());
        List<SysTable> childList = tableDao.select(sysTable);
        // 2.遍历所有一级父节点，查询下面的子节点并赋值
        if (childList != null && childList.size() > 0) {
            // 给当前父节点，设置子节点列表
            List<TableTree> trees = ConvertTable2Tree(childList);
            parent.setChildren(trees);
            trees.forEach(child -> {
                setChildList(child);
            });
        }
    }

    /** 把table表数据，转变为前端需要的treeData格式数据*/
    private List<TableTree> ConvertTable2Tree(List<SysTable> tables) {
        List<TableTree> trees = new ArrayList<>();
        if (tables != null && tables.size() > 0) {
            tables.forEach(item -> {
                TableTree tree = new TableTree();
                tree.setId(item.getId());
                tree.setKey(item.getId().toString());
                tree.setTitle(item.getTableName());
                Integer type = item.getType();
                tree.setType(type);
                JSONObject slots = new JSONObject();
                if (type != null && type.equals(0)) {
                    slots.put("icon", "folder");
                } else {
                    slots.put("icon", "file");
                }
                tree.setSlots(slots);
                trees.add(tree);
            });
        }
        return trees;
    }

}
